package com.dreamers.pathcore;


import com.dreamers.map.MapArray;

import java.util.LinkedList;

public class AStar {

    private LinkedList<AStarTransform> openList;
    private LinkedList<AStarTransform> closeList;
    AStarTransform startPoint = null;
    AStarTransform endPoint = null;
    boolean isFound = true;

    boolean isLog = false;

    public void setStartPoint(int x, int y) {
        startPoint = new AStarTransform();
        startPoint.setPosition(new Vector2(x, y));
        startPoint.setH(0);
        startPoint.setaStarTransformParent(null);
    }

    public void setEndPoint(int x, int y) {
        endPoint = new AStarTransform();
        endPoint.setPosition(new Vector2(x, y));
    }

    void start() {
        isFound = true;
        openList = new LinkedList<AStarTransform>();
        closeList = new LinkedList<AStarTransform>();

    }

    private int count = 0;


    public void AStarRun() {
        start();
        openList.add(startPoint);
        while (getGValue(startPoint.getPosition(), endPoint.getPosition()) != 0) {
            count++;
            if (isLog) {

                System.out.println("count:" + count);
            }
            if (startPoint.getPosition().getX() + 1 < MapArray.map[0].length && MapArray.map[startPoint.getPosition().getY()][startPoint.getPosition().getX() + 1] == 1 &&
                    notInList(startPoint, closeList, 1, 0)
                    && notInList(startPoint, openList, 1, 0)) {
                AStarTransform tempPoint = getaStarStruct(startPoint, endPoint, startPoint.getPosition().getX() + 1, startPoint.getPosition().getY());
                openList.add(tempPoint);

                if (isLog) {

                    LogUtil.INSTANCE.info("[startPoint.getPosition().getY()][startPoint.getX()+1]");
                }

            }
            if (startPoint.getPosition().getX() - 1 >= 0 && MapArray.map[startPoint.getPosition().getY()][startPoint.getPosition().getX() - 1] == 1 &&
                    notInList(startPoint, closeList, -1, 0)
                    && notInList(startPoint, openList, -1, 0)) {
                AStarTransform tempPoint = getaStarStruct(startPoint, endPoint, startPoint.getPosition().getX() - 1, startPoint.getPosition().getY());
                openList.add(tempPoint);
                if (isLog) {

                    LogUtil.INSTANCE.info("[startPoint.getPosition().getY()][startPoint.getX()-1]");
                }

            }
            if (startPoint.getPosition().getY() + 1 < MapArray.map.length && MapArray.map[startPoint.getPosition().getY() + 1][startPoint.getPosition().getX()] == 1 &&
                    notInList(startPoint, closeList, 0, 1)
                    && notInList(startPoint, openList, 0, 1)) {
                AStarTransform tempPoint = getaStarStruct(startPoint, endPoint, startPoint.getPosition().getX(), startPoint.getPosition().getY() + 1);
                openList.add(tempPoint);

                if (isLog) {

                    LogUtil.INSTANCE.info("[startPoint.getPosition().getY()+1][startPoint.getX()]");
                }
            }
            if (startPoint.getPosition().getY() - 1 >= 0 && MapArray.map[startPoint.getPosition().getY() - 1][startPoint.getPosition().getX()] == 1 &&
                    notInList(startPoint, closeList, 0, -1)
                    && notInList(startPoint, openList, 0, -1)) {
                AStarTransform tempPoint = getaStarStruct(startPoint, endPoint, startPoint.getPosition().getX(), startPoint.getPosition().getY() - 1);
                openList.add(tempPoint);
                if (isLog) {

                    LogUtil.INSTANCE.info("[startPoint.getPosition().getY()-1][startPoint.getX()]");
                }
            }
            closeList.add(startPoint);

            openList.remove(startPoint);
            if (openList.size() == 0) {
                isFound = false;
                if (isLog) {

                    LogUtil.INSTANCE.info("not find path");
                }
                break;
            }
            startPoint = getNewStartPoint();

            if (isLog) {

                LogUtil.INSTANCE.info(startPoint.toString());
            }
        }

        printResult(startPoint);
    }

    private int getGValue(Vector2 p1, Vector2 p2) {
        return Math.abs(p2.getX() - p1.getX()) + Math.abs(p2.getY() - p1.getY());
    }

    private void printResult(AStarTransform startPoint) {
        if (isFound == false)
            return;

        while (startPoint.getaStarTransformParent() != null) {
            for (int i = 0; i < closeList.size(); i++) {
                if (closeList.get(i).getPosition().getX() == startPoint.getaStarTransformParent().getPosition().getX() &&
                        closeList.get(i).getPosition().getY() == startPoint.getaStarTransformParent().getPosition().getY()) {
                    if (isLog) {
                        LogUtil.INSTANCE.info("openList" + startPoint.toString());
                    }
                    startPoint = closeList.get(i);
                    break;
                }
            }
        }
    }

    private AStarTransform getNewStartPoint() {
        AStarTransform startPoint = null;
        int tempf = Integer.MAX_VALUE;
        for (int i = 0; i < openList.size(); i++) {
            if (openList.get(i).getF() < tempf) {
                tempf = openList.get(i).getF();
                startPoint = openList.get(i);
                if (isLog) {

                    LogUtil.INSTANCE.info("tempf:" + tempf);
                }
            }
        }
        return startPoint;
    }


    private AStarTransform getaStarStruct(AStarTransform startPoint, AStarTransform endPoint, int x, int y) {
        AStarTransform tempPoint = new AStarTransform();
        tempPoint.setPosition(new Vector2(x, y));
        tempPoint.setH(startPoint.getH() + 1);
        tempPoint.setaStarTransformParent(startPoint);
        tempPoint.setG(getGValue(tempPoint.getPosition().getX(), tempPoint.getPosition().getY(), endPoint.getPosition().getX(), endPoint.getPosition().getY()));
        tempPoint.setF(tempPoint.getH() + tempPoint.getG());
        return tempPoint;
    }


    private int getGValue(int startX, int startY, int endX, int endY) {
        return Math.abs(endX - startX) + Math.abs(endY - startY);
    }

    private boolean notInList(AStarTransform aStarTransform, LinkedList<AStarTransform> aStarTransformLinkedList, int xoffset, int yoffset) {
        boolean notInCloseList = true;
        for (int i = 0; i < aStarTransformLinkedList.size(); i++) {
            if (openList.get(i).getPosition().getX() == (aStarTransform.getPosition().getX() + xoffset) &&
                    openList.get(i).getPosition().getY() == (aStarTransform.getPosition().getY() + yoffset)
            ) {
                notInCloseList = false;
            }
        }
        return notInCloseList;
    }

}
